package http

import (
	"bytes"
	"encoding/json"
	"errors"
	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
	"gitee.com/xuyiping_admin/pkg/xerr"
	"go.uber.org/zap"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

type ClientService struct {
	authClient *http.Client
}

func NewClientService() *ClientService {
	return &ClientService{
		authClient: &http.Client{
			Timeout: time.Duration(10) * time.Second,
		},
	}
}

type Header struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

func (c *ClientService) doRequest(req *http.Request) ([]byte, error) {
	resp, err := c.authClient.Do(req)
	if err != nil {
		var nErr net.Error
		if errors.As(err, &nErr) && nErr.Timeout() {
			for i := 0; i < 3; i++ {
				time.Sleep(15 * time.Second)
				resp, err = c.authClient.Do(req)
				if err != nil {
					zaplog.Error("ClientService", zap.Any("for", i), zap.Any("authClient.Do", err), zap.Any("req", req))
					if i == 3 {
						return nil, xerr.WithStack(err)
					}
				} else {
					break
				}
			}
		} else {
			zaplog.Error("ClientService", zap.Any("authClient.Do", err))
			return nil, xerr.WithStack(err)
		}
	}

	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		zaplog.Error("ClientService", zap.Any("ioutil.ReadAll", err))
		return nil, xerr.WithStack(err)
	}
	if resp.StatusCode != http.StatusOK {
		if len(b) > 0 {
			return nil, xerr.Customf("err:%v,body:%s", err, string(b))
		} else {
			return nil, xerr.Customf("err:%v", err)
		}
	}
	return b, nil
}

func (c *ClientService) DoGet(url string, headers []*Header) ([]byte, error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		zaplog.Error("DoGet", zap.Any("err", err))
		return nil, err
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/json")
	if headers != nil {
		for _, v := range headers {
			req.Header.Add(v.Key, v.Value)
		}
	}
	return c.doRequest(req)
}

func (c *ClientService) DoPut(url string, body interface{}, headers []*Header) ([]byte, error) {
	b, err := json.Marshal(body)
	if err != nil {
		zaplog.Error("DoPut", zap.Any("err", err))
		return nil, xerr.WithStack(err)
	}

	req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(b))
	if err != nil {
		zaplog.Error("ClientService", zap.Any("DoGet", err))
		return nil, err
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/json")
	if headers != nil {
		for _, v := range headers {
			req.Header.Add(v.Key, v.Value)
		}
	}
	return c.doRequest(req)
}

func (c *ClientService) DoPost(url string, body interface{}, headers []*Header) ([]byte, error) {
	var (
		req *http.Request
		err error
		b   []byte
	)
	if body != nil {
		b, err = json.Marshal(body)
		if err != nil {
			zaplog.Error("DoPost", zap.Any("err", err))
			return nil, xerr.WithStack(err)
		}
		zaplog.Info("DoPost", zap.Any("url", url), zap.Any("body", string(b)))
		req, err = http.NewRequest(http.MethodPost, url, bytes.NewReader(b))
	} else {
		req, err = http.NewRequest(http.MethodPost, url, nil)
	}

	if err != nil {
		zaplog.Error("ClientService", zap.Any("NewRequest", err))
		return nil, xerr.WithStack(err)
	}

	// 设置默认的 Content-Type 和 Accept 头
	req.Header.Add("Accept", "application/json")
	if body != nil {
		req.Header.Add("Content-Type", "application/json")
	}

	if headers != nil {
		for _, v := range headers {
			req.Header.Add(v.Key, v.Value)
		}
	}
	return c.doRequest(req)
}
